<?php

/*
 * To change this l, choose Tools | Templates
 * and open the template in the editor.
 */

class Erra_Core_Lexer {

    public static $includeList = array();
    public static $lexerTime = 0;

    public static function logTime() {
        if (config_get('debug.log_messages') == 1) {
            Erra_Core_Kernel::log('Erra_Core_Lexer::ufmParse() lexical analyzer executed in ' . sprintf("%.3f", self::$lexerTime) . ' ms', 'info');
        }
    }

    public static function ufmParse($input, $container=null) {

        $time_start = microtime_float();

        $output = self::includePhtml($input);
        $output = self::syntaxHelpers($output);
// Lexical rules
        $output = self::accessRules(self::accessRules(self::accessRules($output))); // Up to 3 level of access rules
        $output = self::ifRules($output);
        $output = self::chooseRules($output);
        $output = self::foreachRules($output);
        $output = self::scriptIncludes($output);

        $output = self::cssIncludes($output);
        // $output = self::imageData($output);
        $output = self::setMeta($output);
        $output = self::setRegions($output);
        $output = self::messageRules($output);


        $time_end = microtime_float();
        $time = ($time_end - $time_start) * 1000;

        self::$lexerTime += $time;

        return $output;
    }

    public static function syntaxHelpers($input) {
        $input = str_replace('<?=', '<?php echo ', $input);
        $input = str_replace('<?', '<?php ', $input);
        $input = str_replace('<?php php', '<?php ', $input);
        $input = str_replace('<?endif;', '<?php endif;', $input);
        $input = str_replace('PUBLIC_JS', config_get("path.public_js"), $input);
        $input = str_replace('PUBLIC_IMAGES', config_get("path.public_images"), $input);
        $input = str_replace('PUBLIC_STYLE', config_get("path.public_style"), $input);
        $input = str_replace('PUBLIC_UPLOAD_DIRECTORY', config_get("path.public_upload_directory"), $input);
        $input = str_replace('WEBROOT', config_get("application.http_webroot"), $input);

        return $input;
    }

    public static function setTitle($input) {
        if (preg_match('|<title>(.*?)</title>|ms', $input, $mt)) {
            Erra_Core_HtmlStream::$title = $mt[1];
            $input = str_replace('<title' . $mt[1] . '</title>', '', $input);
        }
        return $input;
    }

    public static function setRegions($input) {
        $region_includes = self::getTagAndProperties('region', $input);
        if (count($region_includes)) {
            foreach ($region_includes as $region_include) {
                if (isset($region_include["name"]) && strlen($region_include["name"])) {
                    $ouput = '<?php echo $this->displayRegion(\'' . $region_include["name"] . '\'); ?>';
                    $input = str_replace($region_include["_full_tag_src"], $ouput, $input);
                }
            }
        }
        return $input;
    }

    public static function messageRules($input) {

        if (preg_match_all('|<message(.*?)/>|ms', $input, $matches)) {
            foreach ($matches[1] as $mtKey => $mt) {
                $replacement = '<message' . $mt . '/>';
                $idCode = str_replace(array('>', '"', "'", ' ', ''), '', $mt);
                $idCode = str_replace(array('id='), '', $idCode);
                $messageCode = '';
                if (Erra_Persistance_Session::bKeyExists('message__' . $idCode)) {
                    $messageCode = Erra_Persistance_Session::get('message__' . $idCode);
                    Erra_Persistance_Session::remove('message__' . $idCode);
                }
                $messageCode = '<div id="TMPMESSAGE_' . uniqid() . '">' . $messageCode . '</div>';
                $input = str_replace($replacement, $messageCode, $input);
            }
        }
        return $input;
    }

    public static function includePhtml($input) {
        if (preg_match_all('|<include(.*?)/>|ms', $input, $matches)) {
            $view_root_directory = Erra_Core_Lexer::getCleanPath(DOCUMENT_ROOT . '/application/Views');
            foreach ($matches[1] as $mtKey => $mt) {
                $replacement = '<include' . $mt . '/>';
                $include_file = str_replace(array('>', '"', "'", ' ', ''), '', $mt);
                $include_file = str_replace(array('src='), '', $include_file);

                $include_path = '';
                if (strstr($include_file, '_') !== FALSE && strstr($include_file, '/') === FALSE && strstr($include_file, '\\') === FALSE) {
                    $include_path = explode('_', $include_file);
                    array_pop($include_path);
                    $include_path = implode($include_path);
                }
                $include_file = Erra_Core_Lexer::getCleanPath($view_root_directory . '/' . $include_path . '/' . $include_file);
                if (file_exists($include_file)) {
                    $contents = '';
                    if (!in_array($include_file, self::$includeList)) {
                        $contents = file_get_contents($include_file);
                        self::$includeList[] = $include_file;
                        $contents = self::ufmParse($contents, DOCUMENT_ROOT);
                    } else {
                        $contents = Erra_Utils_Tools::message('error', 'Inifinite loop recursion while trying to include `' . $include_file . '`');
                    }
                    $input = str_replace($replacement, $contents, $input);
                }
            }
        }
        return $input;
    }

    public static function foreachRules($input) {
        $foreach_rules = self::getTagAndProperties('foreach', $input);
        $phpForeachCode = '';
        if (count($foreach_rules)) {
            foreach ($foreach_rules as $foreach_rule) {
                if ((isset($foreach_rule['var']) || isset($foreach_rule['select'])) && isset($foreach_rule['as'])) {
                    $variable = isset($foreach_rule['var']) ? $foreach_rule['var'] : $foreach_rule['select'];
                    $item = $foreach_rule['as'];
                    $phpForeachCode = '<?php foreach(' . $variable . ' as ' . $item . '): ?>';
                    $phpForeachCode .= $foreach_rule['_inside_tag_src'];
                    $phpForeachCode .= '<?php endforeach; ?>';
                }
            }
            $input = str_replace($foreach_rule['_full_tag_src'], $phpForeachCode, $input);
        }
        return $input;
    }

    public static function ifRules($input) {
        $if_rules = self::getTagAndProperties('if', $input);
        $phpIfCode = '';
        if (count($if_rules)) {
            foreach ($if_rules as $if_rule) {
                if (isset($if_rule['test']) && count($if_rule['test']) && strlen($if_rule['_inside_tag_src'])) {
                    $phpIfCode .= '<?php if(' . $if_rule['test'] . '): ?>';
                    $phpIfCode .= $if_rule['_inside_tag_src'];
                    $phpIfCode .= '<?php endif; ?>';

                    $input = str_replace($if_rule['_full_tag_src'], $phpIfCode, $input);
                }
            }
        }
        return $input;
    }

    public static function chooseRules($input) {
        $choose_rules = self::getTagAndProperties('choose', $input);
        if (count($choose_rules)) {
            foreach ($choose_rules as $choose_rule) {
                $when_otherwise = $choose_rule['_inside_tag_src'];
                $chooseRuleCode = self::whenRules($when_otherwise);
                $chooseRuleCode = self::otherwiseRules($chooseRuleCode);
                $chooseRuleCode .= "\n" . '<?php endif; ?>' . "\n";
            }

            $input = str_replace($choose_rule['_full_tag_src'], $chooseRuleCode, $input);
        }
        return $input;
    }

    public static function otherwiseRules($input) {
        $otherwise_rules = self::getTagAndProperties('otherwise', $input);
        if (count($otherwise_rules)) {
            foreach ($otherwise_rules as $otherwise_rule) {
                if (strlen($otherwise_rule['_inside_tag_src'])) {
                    $phpOtherwiseCode = '<?php else: ?>';
                    $phpOtherwiseCode .= $otherwise_rule['_inside_tag_src'];

                    $input = str_replace($otherwise_rule['_full_tag_src'], $phpOtherwiseCode, $input);
                }
            }
        }
        return $input;
    }

    public static function whenRules($input) {
        $when_rules = self::getTagAndProperties('when', $input);
        if (count($when_rules)) {
            foreach ($when_rules as $itemNumber => $when_rule) {

                if (isset($when_rule['test']) && count($when_rule['test']) && strlen($when_rule['_inside_tag_src'])) {
                    $elseIfStatement = ($itemNumber > 0) ? 'else' : '';
                    $phpWhenCode = '<?php ' . $elseIfStatement . 'if(' . $when_rule['test'] . '): ?>';
                    $phpWhenCode .= $when_rule['_inside_tag_src'];

                    $input = str_replace($when_rule['_full_tag_src'], $phpWhenCode, $input);
                }
            }
        }
        return $input;
    }

    public static function accessRules($input) {
        $access_control_rules = self::getTagAndProperties('access_control', $input);
        if (count($access_control_rules)) {
            foreach ($access_control_rules as $access_rule) {
                $phpAclCode = '';
                if (isset($access_rule['grant']) && !empty($access_rule['grant'])) {
                    $phpAclCode .= '<?php if(';
                    $acl = explode(',', $access_rule['grant']);
                    foreach ($acl as $itemCount => $access_to_check) {
                        $access_to_check = str_replace(array('(', ')'), array('', ''), trim($access_to_check));
                        if (strstr($access_to_check, '!') === FALSE) {
                            $phpAclCode .= 'Erra_System_AccessControl::is("' . $access_to_check . '")';
                        } else {
                            $access_to_check = str_replace('!', '', $access_to_check);
                            $phpAclCode .= '!Erra_System_AccessControl::is("' . $access_to_check . '")';
                        }
                        $phpAclCode .= ($itemCount == count($acl) - 1 ) ? '): ?>' : ' || ';
                    }
// Include inside tag if access is granted
                    $phpAclCode .= $access_rule['_inside_tag_src'];

// If there is on_deny, include else
                    $replace_code = '';
                    if (isset($access_rule['on_deny']) && strstr($access_rule['on_deny'], '.phtml') !== FALSE) {
                        $phpAclCode .= "\n" . '<?php else: ?>' . "\n";

                        $include_file = $access_rule['on_deny'];

                        $include_file = Erra_Core_Lexer::getCleanPath(DOCUMENT_ROOT . '/application/Views/' . $include_file);
                        $include_content = '';
                        if (file_exists($include_file)) {
                            $replace_code = file_get_contents($include_file);
                            $replace_code = self::ufmParse($replace_code, DOCUMENT_ROOT);
                        }
                    }
                    $phpAclCode .= $replace_code;
                    $phpAclCode .= "\n" . '<?php endif; ?>' . "\n";

                    $input = str_replace($access_rule['_full_tag_src'], $phpAclCode, $input);
                } else {
// grant is empty
                    $warning_message = (empty($acl['grant'])) ? Erra_Utils_Tools::message('warning', '`access_control` tag is not correctly set ! Ignoring this section...') : '';
                    $input = str_replace($access_rule['_full_tag_src'], $warning_message, $input);
                }
            }
        }
        return $input;
    }

    public static function scriptIncludes($input) {
        $script_includes = self::getTagAndProperties('script', $input);
        if (count($script_includes)) {
            foreach ($script_includes as $script_include) {
                if (isset($script_include["src"]) && isset($script_include["type"])) {
                    if (strstr($script_include["type"], 'text/javascript') && strlen($script_include["src"])) {
                        $src = $script_include["src"];
                        $src = (strpos($src, 'js/') == 0 && !strstr($src, 'http')) ? config_get('application.http_webroot') . '/' . $src : $src;
                        Erra_Core_HtmlStream::$js[] = $src;
                        $input = str_replace($script_include["_full_tag_src"], '', $input);
                    }
                }
            }
        }
        return $input;
    }

    public static function cssIncludes($input) {
        $css_includes = self::getShortTagAndProperties('link', $input);
        if (count($css_includes)) {
            foreach ($css_includes as $css_include) {
                if (isset($css_include["type"]) && isset($css_include["href"])) {
                    if (strstr($css_include["type"], 'text/css') && strlen($css_include["href"])) {
                        $href = $css_include["href"];
                        $pos = strpos($href, 'style/');
                        $href = (is_numeric($pos) && $pos == 0) ? config_get('application.http_webroot') . '/' . $href : $href;
                        Erra_Core_HtmlStream::$css[] = $href;
                        $input = str_replace($css_include["_full_tag_src"], '', $input);
                    }
                }
            }
        }
        return $input;
    }

    public static function imageData($input) {
        $img_tags = self::getShortTagAndProperties('img', $input);
        if (count($img_tags)) {
            foreach ($img_tags as $img_tag) {
                if (isset($img_tag["src"])) {
                    $imgSrc = $img_tag["src"];
                    $pos = strpos($imgSrc, 'images/');
                    if (is_numeric($pos) && $pos == 0) {
                        $newImg = config_get('application.http_webroot') . '/' . $imgSrc;
                        $input = str_replace($imgSrc, $newImg, $input);
                    }

                    $pos = strpos($imgSrc, 'image/');
                    if (is_numeric($pos) && $pos == 0) {
                        $newImg = config_get('application.http_webroot') . '/' . $imgSrc;
                        $input = str_replace($imgSrc, $newImg, $input);
                    }
                }
            }
        }
        return $input;
    }

    public static function setMeta($input) {
        $meta_items = self::getShortTagAndProperties('meta', $input);
        if (count($meta_items)) {
            foreach ($meta_items as $meta_item) {
                if (isset($meta_item["name"]) && isset($meta_item["content"])) {
                    Erra_Core_HtmlStream::$meta[$meta_item["name"]] = $meta_item["content"];
                }
            }
        }
        return $input;
    }

    public static function getCleanPath($directory) {
// Get system separator depend the first seprarator position
        $dirSeparator = (strpos($directory, trim('\ ')) === false) ? '/' : trim('\ ');

// Now convert the path to unix system
        $directory = str_replace(trim('\ '), '/', $directory);
        $directory = explode('/', $directory);
        $result_directory = '';
        $num_dir = count($directory);
        for ($i = 0; $i < $num_dir; $i++) {
// Don't finish directory with a trailing slash
            $addSeparator = ($num_dir - 1 == $i) ? '' : $dirSeparator;
            $result_directory .= $directory[$i] . $addSeparator;
        }
// Avoid double slashes
        $result_directory = str_replace($dirSeparator . $dirSeparator, $dirSeparator, $result_directory);
        return $result_directory;
    }

    public static function getShortTagAndProperties($tagName, $input) {
        $result = array();
        if (preg_match_all('|<' . $tagName . '(.*?)>|ms', $input, $matches)) {
            foreach ($matches[1] as $mtKey => $mt) {
                $insideTag = $mt;
                $full_tag = '<' . $insideTag . '/>';

                $tag = array();
                $tag['_tag_name'] = $tagName;
                $tag['_full_tag_src'] = $full_tag;

                $insideTag = rtrim(trim($insideTag), '/');
// tagName='tagValue'
                if (preg_match_all('|(([^\'\"]*)=[\']([^\']*)[\'])|ms', $insideTag, $tagPropertiesMatches)) {
                    foreach ($tagPropertiesMatches[2] as $tagKey => $tagPropertyName) {
                        $tagPropertyName = trim($tagPropertyName);
                        $tagPropertyValue = trim($tagPropertiesMatches[3][$tagKey]);
                        $tag[$tagPropertyName] = $tagPropertyValue;
                    }
                }

// tagName="tagValue"
                if (preg_match_all('|(([^\"\']*)=[\"]([^\"]*)[\"])|ms', $insideTag, $tagPropertiesMatches)) {
                    foreach ($tagPropertiesMatches[2] as $tagKey => $tagPropertyName) {
                        $tagPropertyName = trim($tagPropertyName);
                        $tagPropertyValue = trim($tagPropertiesMatches[3][$tagKey]);
                        $tag[$tagPropertyName] = $tagPropertyValue;
                    }
                }
                $result[] = $tag;
            }
        }
        return $result;
    }

    public static function getTagAndProperties($tagName, $input) {
        $result = array();
        if (preg_match_all('|<' . $tagName . '(.*?)>(.*?)</' . $tagName . '>|ms', $input, $matches)) {
            foreach ($matches[1] as $mtKey => $mt) {
                $tagProperties = $mt;
                $inside_tag = $matches[2][$mtKey];
                $full_tag = '<' . $tagName . $tagProperties . '>' . $inside_tag . '</' . $tagName . '>';

                $tag = array();
                $tag['_tag_name'] = $tagName;
                $tag['_full_tag_src'] = $full_tag;
                $tag['_inside_tag_src'] = $inside_tag;

// tagName='tagValue'
                if (preg_match_all('|(([^\'\"]*)=[\']([^\']*)[\'])|ms', $tagProperties, $tagPropertiesMatches)) {
                    foreach ($tagPropertiesMatches[2] as $tagKey => $tagPropertyName) {
                        $tagPropertyName = trim($tagPropertyName);
                        $tagPropertyValue = trim($tagPropertiesMatches[3][$tagKey]);
                        $tag[$tagPropertyName] = $tagPropertyValue;
                    }
                }

// tagName="tagValue"
                if (preg_match_all('|(([^\"\']*)=[\"]([^\"]*)[\"])|ms', $tagProperties, $tagPropertiesMatches)) {
                    foreach ($tagPropertiesMatches[2] as $tagKey => $tagPropertyName) {
                        $tagPropertyName = trim($tagPropertyName);
                        $tagPropertyValue = trim($tagPropertiesMatches[3][$tagKey]);
                        $tag[$tagPropertyName] = $tagPropertyValue;
                    }
                }
                $result[] = $tag;
            }
        }
        return $result;
    }

}

?>
